package cn.itsource.ymcc.service.impl;

import cn.itsource.ymcc.constant.RConst;
import cn.itsource.ymcc.domain.AggrTermsBucket;
import cn.itsource.ymcc.domain.CourseDoc;
import cn.itsource.ymcc.errorcode.ErrorCode;
import cn.itsource.ymcc.exception.GlobalException;
import cn.itsource.ymcc.query.AggrPageList;
import cn.itsource.ymcc.query.CourseDocQuery;
import cn.itsource.ymcc.repository.CourseElasticsearchRepository;
import cn.itsource.ymcc.repository.HighlightResultMapper;
import cn.itsource.ymcc.result.JsonResult;
import cn.itsource.ymcc.result.PageList;
import cn.itsource.ymcc.service.IESearchService;
import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.BucketOrder;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class ESearchServiceImpl implements IESearchService {
    @Autowired
    private CourseElasticsearchRepository elasticsearchRepository;

    @Autowired
    private ElasticsearchRestTemplate   restTemplate;

    @Autowired
    private HighlightResultMapper highlightResultMapper;
    @Override
    public JsonResult save(List<CourseDoc> courseDocs) {
        elasticsearchRepository.saveAll(courseDocs);
        return JsonResult.success();
    }


    @Override
    public JsonResult delete(List<Long> courseIds) {
        for (Long courseId : courseIds) {
            elasticsearchRepository.deleteById(courseId);
        }
        return JsonResult.success();
    }

    @Override
    public JsonResult search(CourseDocQuery query) {

        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();

        BoolQueryBuilder bool = QueryBuilders.boolQuery();
        if (StringUtils.hasText(query.getKeyword())) {
            bool.filter().add(QueryBuilders.matchQuery("name", query.getKeyword()));
        }
        if (StringUtils.hasText(query.getGradeName())) {
            bool.filter().add(QueryBuilders.termQuery("gradeName", query.getGradeName()));
        }
        queryBuilder.withQuery(bool);

        if (StringUtils.hasText(query.getOrderField())) {
            SortOrder sortOrder = SortOrder.ASC;
            if ("desc".equalsIgnoreCase(query.getOrderType())) {
                sortOrder = SortOrder.DESC;
            }
            SortBuilder sortBuilder = SortBuilders.fieldSort(query.getOrderField()).order(sortOrder);
            queryBuilder.withSort(sortBuilder);
        }
        //分页  注意：SpringDataElasticsearch的当前页码是从0开始的
        Pageable pageable = PageRequest.of(query.getPageNo() <= RConst.Num.INT_0 ? RConst.Num.INT_0 : query.getPageNo() - RConst.Num.INT_1, query.getPageSize());
        queryBuilder.withPageable(pageable);

        //高亮
        HighlightBuilder.Field  highlightField = new HighlightBuilder.Field("name")
                .preTags("<span style='color:red;background-color:skyblue'>")
                .postTags("</span>");
        queryBuilder.withHighlightFields(highlightField);

        //聚合查询,按照机构名字聚合，安装数量倒排
        queryBuilder.addAggregation(AggregationBuilders
                .terms("gradeName") //聚合名字
                .field("gradeName")	//聚合的字段
                .order(BucketOrder.count(false)).size(20));//排序后取前20条
        //2.调用repository的search方法查询
        //Page<CourseDoc> page = elasticsearchRepository.search(queryBuilder.build());
        AggregatedPage<CourseDoc> page = restTemplate.queryForPage(queryBuilder.build(), CourseDoc.class, highlightResultMapper);


        System.out.println("当前页码："+page.getNumber());
        System.out.println("每页条数："+page.getSize());
        System.out.println("总条数："+page.getTotalElements());
        System.out.println("总页数："+page.getTotalPages());
        System.out.println("当前页条数："+page.getNumberOfElements());
        //封装返回数据
        List<CourseDoc> content = page.getContent();
//        PageList<CourseDoc> pageList = new PageList<>(page.getTotalElements(),content);
        Map<String, List<AggrTermsBucket>> aggrResult = getAggrResult(page);
        AggrPageList<CourseDoc> courseDocAggrPageList = new AggrPageList<>(page.getTotalElements(), content, aggrResult);
        return JsonResult.success().setData(courseDocAggrPageList);
    }

    @Override
    public JsonResult selectOneByCourseId(Long courseId) {
        Optional<CourseDoc> byId = elasticsearchRepository.findById(courseId);
        CourseDoc courseDoc = byId.get();
        return JsonResult.success().setData(courseDoc);
    }

    @Override
    public JsonResult getCourseInfo(List<Long> courseIds) {
        Iterable<CourseDoc> allById = elasticsearchRepository.findAllById(courseIds);
        ArrayList<CourseDoc> courseDocs = Lists.newArrayList(allById);
        if (courseDocs.isEmpty()){
            throw new GlobalException(ErrorCode.GET_COURSE_DETAIL_ERROR);
        }
        return JsonResult.success().setData(courseDocs);
    }

    @Override
    public JsonResult selectbatchById(List<Long> courseIds) {
        Iterable<CourseDoc> allById = elasticsearchRepository.findAllById(courseIds);
        ArrayList<CourseDoc> courseDocs = Lists.newArrayList(allById);
        if (courseDocs.isEmpty()){
            throw new GlobalException(ErrorCode.GET_COURSE_DETAIL_ERROR);
        }
        return JsonResult.success().setData(JSON.toJSONString(courseDocs));
    }

    /**
     * 聚合结果封装
     * @param page
     * @return
     */
    private Map<String, List<AggrTermsBucket>> getAggrResult(AggregatedPage<CourseDoc> page) {
        //用来封装聚合结果
        Map<String,List<AggrTermsBucket>> aggrMap = new HashMap<>();

        //遍历聚合结果
        Aggregations aggregations = page.getAggregations();

        Map<String,Aggregation> stringAggregationMap = aggregations.asMap();

        Set<Map.Entry<String,Aggregation>> entries = stringAggregationMap.entrySet();

        entries.forEach(e->{
            //集合的名字
            String key = e.getKey();
            //集合的值的处理
            ParsedStringTerms value = (ParsedStringTerms)e.getValue();
            List<? extends Terms.Bucket> buckets = value.getBuckets();

            //使用lomda 把  List<StringTerms.Bucket>  转成  List<AggrTermsVo>
            List<AggrTermsBucket> aggrTermsVos = buckets.stream().map(bucket -> {
                return new AggrTermsBucket(bucket.getKeyAsString(), bucket.getDocCount());
            }).collect(Collectors.toList());

            aggrMap.put(key, aggrTermsVos);
        });
        return aggrMap;
    }
}
